/*
 * @Author: xiaosihan
 * @Date: 2021-07-31 13:04:11
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-07-31 21:17:58
 */

import * as THREE from "three";
import { BufferGeometry, Camera, Group, Material, Scene, WebGLRenderer } from "three";

/**
 * let lightColumn= new LightColumn("#f0f");
 * lightColumn.setColor("#043592");
 * lightColumn.position.set(0, 0, 0);
 * lightColumn.scale.set(1, 1, 1);   
 * this.scene.add(lightColumn);     
 */
export default class LightColumn extends THREE.Object3D {

    constructor(color = "#fff", delay = Math.random()) {
        super();
        this.init();
        this.setColor(color);
        this.property.delay = 0;
    }

    property = {
        color: "#fff",
        delay: 0
    }

    cacheProperty = {
        color: "#fff"
    };

    // 后期修改颜色要调用这个方法
    setColor(color = "#fff") {
        this.property.color = color;
    }

    init() {
        this.add(this.smallbal);
        this.add(this.tail);
        this.tail.scale.set(0.03, 0.5, 0.03);
        this.tail.material.uniforms.istail.value = true;

        // 时时计算精灵模型的渲染顺序 距离相机近越近 渲染顺序就越大
        this.smallbal.onAfterRender =
            (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => {
                camera.getWorldPosition(LightColumn.cameraPosition);
                this.smallbal.getWorldPosition(LightColumn.vector3Temp);
                this.smallbal.renderOrder = -LightColumn.cameraPosition.distanceTo(LightColumn.vector3Temp) + 0.01;

                let timetemp = (performance.now() / 2000 + this.property.delay) % 1;

                this.smallbal.position.setY(Math.min(0.7, timetemp));
                let scale = Math.max((timetemp - 0.7) * 2, 0.1);
                this.smallbal.scale.set(scale, scale, scale);
                this.smallbal.material.uniforms.opacity.value = Math.min(1.0, (1.0 - timetemp) * 7);

                // 更新颜色
                if (this.cacheProperty.color !== this.property.color) {
                    // this.cacheProperty.color = this.property.color;
                    this.smallbal.material.uniforms.color.value.set(this.property.color);
                }

            }

        this.tail.onAfterRender =
            (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => {
                camera.getWorldPosition(LightColumn.cameraPosition);
                this.tail.getWorldPosition(LightColumn.vector3Temp);
                this.tail.renderOrder = -LightColumn.cameraPosition.distanceTo(LightColumn.vector3Temp) - 0.01;

                let timetemp = (performance.now() / 2000 + this.property.delay) % 1;

                this.tail.position.setY(Math.min(0.7, timetemp));

                this.tail.material.uniforms.opacity.value = (timetemp > 0.7 ? 0 : 1);

                // 更新颜色
                if (this.cacheProperty.color !== this.property.color) {
                    // this.cacheProperty.color = this.property.color;
                    this.tail.material.uniforms.color.value.set(this.property.color);
                }

            }


    }

    // 小球材质
    static ballMaterial = new THREE.ShaderMaterial({
        transparent: true,
        side: THREE.FrontSide,
        uniforms: {
            color: {
                value: new THREE.Color("#fff")
            },
            coeficient: {
                value: 0
            },
            power: {
                value: 1
            },
            opacity: {
                value: 1
            },
            istail: {
                value: false
            }
        },
        vertexShader: `
            varying vec3  vVertexWorldPosition;
            varying vec3  vVertexNormal;
            varying vec4  vFragColor;
            varying vec3  vPosition;
            void main(){
                vPosition = position;
                //将法线转换到视图坐标系中
                vVertexNormal = normalize(normalMatrix * normal);
                //将顶点转换到世界坐标系中
                vVertexWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
            `,
        fragmentShader: `
            uniform vec3 color;
            uniform float coeficient;
            uniform float power;
            uniform float opacity;
            uniform bool istail;
            
            varying vec3  vVertexNormal;
            varying vec3  vVertexWorldPosition;
            varying vec3  vPosition;
            
            varying vec4  vFragColor;
            
            void main(){
                //世界坐标系中顶点位置到相机位置到的距离
                vec3 worldVertexToCamera = cameraPosition - vVertexWorldPosition;
                //视图坐标系中
                vec3 viewCameraToVertex = (viewMatrix * vec4(worldVertexToCamera, 0.0)).xyz; 
                viewCameraToVertex = normalize(viewCameraToVertex);
                float intensity = coeficient + dot(vVertexNormal, viewCameraToVertex);

                intensity = intensity * intensity * intensity * intensity;

                vec3 fragcolor1 = vec3(1.0) * intensity;

                vec3 fragcolor2 = color * (1.0 - intensity);

                vec3 fragcolor = clamp(fragcolor1, 0.0, 0.8) + clamp(fragcolor2, 0.0, 1.0);

                gl_FragColor = vec4(fragcolor,  clamp(intensity, 0.0, 1.0) * opacity);

                if(istail && (vVertexWorldPosition.y < 0.0 || vPosition.y > 0.0)){
                    discard;
                }

            }
            `
    });

    // 相机的位置
    static cameraPosition = new THREE.Vector3();

    // 精灵模型的位置
    static vector3Temp = new THREE.Vector3();

    // 小球模型
    static ballGeo = new THREE.SphereBufferGeometry(1, 100, 100);

    smallbal = new THREE.Mesh(
        LightColumn.ballGeo,
        LightColumn.ballMaterial.clone()
    );

    tail = new THREE.Mesh(
        LightColumn.ballGeo,
        LightColumn.ballMaterial.clone()
    );

}